/*
* Surf.java
* Copyright (C) 2003
*
* $Id: Surf.java,v 1.6 2005/01/09 22:34:21 cawe Exp $
*/
/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
using System;
using Defines = Suake2.UI.Defines;
using Suake2.UI.client;
using cplane_t = Suake2.UI.game.cplane_t;
using Com = Suake2.UI.qcommon.Com;
using Suake2.UI.render;
using Lib = Suake2.UI.util.Lib;
using Math3D = Suake2.UI.util.Math3D;
//UPGRADE_TODO: The type 'java.nio.ByteOrder' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using ByteOrder = java.nio.ByteOrder;
//UPGRADE_TODO: The type 'java.nio.IntBuffer' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using IntBuffer = java.nio.IntBuffer;
using GL = net.java.games.jogl.GL;
namespace Suake2.UI.render.jogl
{
	
	/// <summary> Surf
	/// 
	/// </summary>
	/// <author>  cwei
	/// </author>
	public abstract class Surf:Draw
	{
		public Surf()
		{
			InitBlock();
		}
		private void  InitBlock()
		{
			gl_lms = new gllightmapstate_t();
			temp2 = Lib.newIntBuffer(34 * 34, ByteOrder.LITTLE_ENDIAN);
			temp = Lib.newIntBuffer(128 * 128, ByteOrder.LITTLE_ENDIAN);
			fatvis = new sbyte[Defines.MAX_MAP_LEAFS / 8];
		}
		
		// GL_RSURF.C: surface-related refresh code
		internal float[] modelorg = new float[]{0, 0, 0}; // relative to viewpoint
		
		internal msurface_t r_alpha_surfaces;
		
		internal const int DYNAMIC_LIGHT_WIDTH = 128;
		internal const int DYNAMIC_LIGHT_HEIGHT = 128;
		
		internal const int LIGHTMAP_BYTES = 4;
		
		new internal const int BLOCK_WIDTH = 128;
		new internal const int BLOCK_HEIGHT = 128;
		
		internal const int MAX_LIGHTMAPS = 128;
		
		new internal int c_visible_lightmaps;
		new internal int c_visible_textures;
		
		//UPGRADE_NOTE: Final was removed from the declaration of 'GL_LIGHTMAP_FORMAT '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		internal static readonly int GL_LIGHTMAP_FORMAT = net.java.games.jogl.GL_Fields.GL_RGBA;
		
		internal class gllightmapstate_t
		{
			private void  InitBlock()
			{
				lightmap_surfaces = new msurface_t[jake2.render.jogl.Surf.MAX_LIGHTMAPS];
				allocated = new int[jake2.render.jogl.Surf.BLOCK_WIDTH];
				lightmap_buffer = Lib.newIntBuffer(jake2.render.jogl.Surf.BLOCK_WIDTH * jake2.render.jogl.Surf.BLOCK_HEIGHT, ByteOrder.LITTLE_ENDIAN);
			}
			internal int internal_format;
			internal int current_lightmap_texture;
			
			//UPGRADE_NOTE: The initialization of  'lightmap_surfaces' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
			internal msurface_t[] lightmap_surfaces;
			//UPGRADE_NOTE: The initialization of  'allocated' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
			internal int[] allocated;
			
			// the lightmap texture data needs to be kept in
			// main memory so texsubimage can update properly
			//UPGRADE_NOTE: The initialization of  'lightmap_buffer' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
			internal IntBuffer lightmap_buffer;
			
			
			public gllightmapstate_t()
			{
				for (int i = 0; i < jake2.render.jogl.Surf.MAX_LIGHTMAPS; i++)
					lightmap_surfaces[i] = new msurface_t();
			}
			
			public virtual void  clearLightmapSurfaces()
			{
				for (int i = 0; i < jake2.render.jogl.Surf.MAX_LIGHTMAPS; i++)
				// TODO lightmap_surfaces[i].clear();
					lightmap_surfaces[i] = new msurface_t();
			}
		}
		
		//UPGRADE_NOTE: The initialization of  'gl_lms' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal gllightmapstate_t gl_lms;
		
		//
		//	static void		LM_InitBlock( void );
		//	static void		LM_UploadBlock( qboolean dynamic );
		//	static qboolean	LM_AllocBlock (int w, int h, int *x, int *y);
		//
		//	extern void R_SetCacheState( msurface_t *surf );
		//	extern void R_BuildLightMap (msurface_t *surf, byte *dest, int stride);
		//
		
		// Model.java
		internal abstract sbyte[] Mod_ClusterPVS(int cluster, model_t model);
		// Warp.java
		internal abstract void  R_DrawSkyBox();
		internal abstract void  R_AddSkySurface(msurface_t surface);
		internal abstract void  R_ClearSkyBox();
		internal abstract void  EmitWaterPolys(msurface_t fa);
		// Light.java
		internal abstract void  R_MarkLights(dlight_t light, int bit, mnode_t node);
		internal abstract void  R_SetCacheState(msurface_t surf);
		internal abstract void  R_BuildLightMap(msurface_t surf, IntBuffer dest, int stride);
		
		/*
		=============================================================
		
		BRUSH MODELS
		
		=============================================================
		*/
		
		/*
		===============
		R_TextureAnimation
		
		Returns the proper texture for a given time and base texture
		===============
		*/
		internal virtual image_t R_TextureAnimation(mtexinfo_t tex)
		{
			int c;
			
			if (tex.next == null)
				return tex.image;
			
			c = currententity.frame % tex.numframes;
			while (c != 0)
			{
				tex = tex.next;
				c--;
			}
			
			return tex.image;
		}
		
		/*
		================
		DrawGLPoly
		================
		*/
		internal virtual void  DrawGLPoly(glpoly_t p)
		{
			gl.glBegin(net.java.games.jogl.GL_Fields.GL_POLYGON);
			for (int i = 0; i < p.numverts; i++)
			{
				gl.glTexCoord2f(p.s1(i), p.t1(i));
				gl.glVertex3f(p.x(i), p.y(i), p.z(i));
			}
			gl.glEnd();
		}
		
		//	  ============
		//	  PGM
		/*
		================
		DrawGLFlowingPoly -- version of DrawGLPoly that handles scrolling texture
		================
		*/
		internal virtual void  DrawGLFlowingPoly(msurface_t fa)
		{
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			float scroll = (- 64) * ((r_newrefdef.time / 40.0f) - (int) (r_newrefdef.time / 40.0f));
			if (scroll == 0.0f)
				scroll = - 64.0f;
			
			gl.glBegin(net.java.games.jogl.GL_Fields.GL_POLYGON);
			glpoly_t p = fa.polys;
			for (int i = 0; i < p.numverts; i++)
			{
				gl.glTexCoord2f(p.s1(i) + scroll, p.t1(i));
				gl.glVertex3f(p.x(i), p.y(i), p.z(i));
			}
			gl.glEnd();
		}
		//	  PGM
		//	  ============
		
		/*
		** R_DrawTriangleOutlines
		*/
		internal virtual void  R_DrawTriangleOutlines()
		{
			if (gl_showtris.value_Renamed == 0)
				return ;
			
			gl.glDisable(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D);
			gl.glDisable(net.java.games.jogl.GL_Fields.GL_DEPTH_TEST);
			gl.glColor4f(1, 1, 1, 1);
			
			for (int i = 0; i < MAX_LIGHTMAPS; i++)
			{
				msurface_t surf;
				
				for (surf = gl_lms.lightmap_surfaces[i]; surf != null; surf = surf.lightmapchain)
				{
					glpoly_t p = surf.polys;
					for (; p != null; p = p.chain)
					{
						for (int j = 2; j < p.numverts; j++)
						{
							gl.glBegin(net.java.games.jogl.GL_Fields.GL_LINE_STRIP);
							gl.glVertex3f(p.x(0), p.y(0), p.z(0));
							gl.glVertex3f(p.x(j - 1), p.y(j - 1), p.z(j - 1));
							gl.glVertex3f(p.x(j), p.y(j), p.z(j));
							gl.glVertex3f(p.x(0), p.y(0), p.z(0));
							gl.glEnd();
						}
					}
				}
			}
			gl.glEnable(net.java.games.jogl.GL_Fields.GL_DEPTH_TEST);
			gl.glEnable(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D);
		}
		
		/*
		** DrawGLPolyChain
		*/
		internal virtual void  DrawGLPolyChain(glpoly_t p, float soffset, float toffset)
		{
			if (soffset == 0 && toffset == 0)
			{
				for (; p != null; p = p.chain)
				{
					gl.glBegin(net.java.games.jogl.GL_Fields.GL_POLYGON);
					for (int j = 0; j < p.numverts; j++)
					{
						gl.glTexCoord2f(p.s2(j), p.t2(j));
						gl.glVertex3f(p.x(j), p.y(j), p.z(j));
					}
					gl.glEnd();
				}
			}
			else
			{
				for (; p != null; p = p.chain)
				{
					gl.glBegin(net.java.games.jogl.GL_Fields.GL_POLYGON);
					for (int j = 0; j < p.numverts; j++)
					{
						gl.glTexCoord2f(p.s2(j) - soffset, p.t2(j) - toffset);
						gl.glVertex3f(p.x(j), p.y(j), p.z(j));
					}
					gl.glEnd();
				}
			}
		}
		
		/*
		** R_BlendLightMaps
		**
		** This routine takes all the given light mapped surfaces in the world and
		** blends them into the framebuffer.
		*/
		internal virtual void  R_BlendLightmaps()
		{
			int i;
			msurface_t surf;
			msurface_t newdrawsurf = null;
			
			// don't bother if we're set to fullbright
			if (r_fullbright.value_Renamed != 0)
				return ;
			if (r_worldmodel.lightdata == null)
				return ;
			
			// don't bother writing Z
			gl.glDepthMask(false);
			
			/*
			** set the appropriate blending mode unless we're only looking at the
			** lightmaps.
			*/
			if (gl_lightmap.value_Renamed == 0)
			{
				gl.glEnable(net.java.games.jogl.GL_Fields.GL_BLEND);
				
				if (gl_saturatelighting.value_Renamed != 0)
				{
					gl.glBlendFunc(net.java.games.jogl.GL_Fields.GL_ONE, net.java.games.jogl.GL_Fields.GL_ONE);
				}
				else
				{
					char format = gl_monolightmap.string_Renamed.ToUpper()[0];
					if (format != '0')
					{
						switch (format)
						{
							
							case 'I': 
								gl.glBlendFunc(net.java.games.jogl.GL_Fields.GL_ZERO, net.java.games.jogl.GL_Fields.GL_SRC_COLOR);
								break;
							
							case 'L': 
								gl.glBlendFunc(net.java.games.jogl.GL_Fields.GL_ZERO, net.java.games.jogl.GL_Fields.GL_SRC_COLOR);
								break;
							
							case 'A': 
							default: 
								gl.glBlendFunc(net.java.games.jogl.GL_Fields.GL_SRC_ALPHA, net.java.games.jogl.GL_Fields.GL_ONE_MINUS_SRC_ALPHA);
								break;
							}
					}
					else
					{
						gl.glBlendFunc(net.java.games.jogl.GL_Fields.GL_ZERO, net.java.games.jogl.GL_Fields.GL_SRC_COLOR);
					}
				}
			}
			
			if (currentmodel == r_worldmodel)
				c_visible_lightmaps = 0;
			
			/*
			** render static lightmaps first
			*/
			for (i = 1; i < MAX_LIGHTMAPS; i++)
			{
				if (gl_lms.lightmap_surfaces[i] != null)
				{
					if (currentmodel == r_worldmodel)
						c_visible_lightmaps++;
					
					GL_Bind(gl_state.lightmap_textures + i);
					
					for (surf = gl_lms.lightmap_surfaces[i]; surf != null; surf = surf.lightmapchain)
					{
						if (surf.polys != null)
							DrawGLPolyChain(surf.polys, 0, 0);
					}
				}
			}
			
			
			// TODO impl: render dynamic lightmaps
			
			/*
			** render dynamic lightmaps
			*/
			if (gl_dynamic.value_Renamed != 0)
			{
				LM_InitBlock();
				
				GL_Bind(gl_state.lightmap_textures + 0);
				
				if (currentmodel == r_worldmodel)
					c_visible_lightmaps++;
				
				newdrawsurf = gl_lms.lightmap_surfaces[0];
				
				for (surf = gl_lms.lightmap_surfaces[0]; surf != null; surf = surf.lightmapchain)
				{
					int smax, tmax;
					IntBuffer base_Renamed;
					
					smax = (surf.extents[0] >> 4) + 1;
					tmax = (surf.extents[1] >> 4) + 1;
					
					pos_t lightPos = new pos_t(surf.dlight_s, surf.dlight_t);
					
					if (LM_AllocBlock(smax, tmax, lightPos))
					{
						// kopiere die koordinaten zurueck
						surf.dlight_s = lightPos.x;
						surf.dlight_t = lightPos.y;
						
						base_Renamed = gl_lms.lightmap_buffer;
						base_Renamed.position(surf.dlight_t * BLOCK_WIDTH + surf.dlight_s);
						
						R_BuildLightMap(surf, base_Renamed.slice(), BLOCK_WIDTH);
					}
					else
					{
						msurface_t drawsurf;
						
						// upload what we have so far
						LM_UploadBlock(true);
						
						// draw all surfaces that use this lightmap
						for (drawsurf = newdrawsurf; drawsurf != surf; drawsurf = drawsurf.lightmapchain)
						{
							if (drawsurf.polys != null)
								DrawGLPolyChain(drawsurf.polys, (drawsurf.light_s - drawsurf.dlight_s) * (1.0f / 128.0f), (drawsurf.light_t - drawsurf.dlight_t) * (1.0f / 128.0f));
						}
						
						newdrawsurf = drawsurf;
						
						// clear the block
						LM_InitBlock();
						
						// try uploading the block now
						if (!LM_AllocBlock(smax, tmax, lightPos))
						{
							Com.Error(Defines.ERR_FATAL, "Consecutive calls to LM_AllocBlock(" + smax + "," + tmax + ") failed (dynamic)\n");
						}
						
						// kopiere die koordinaten zurueck
						surf.dlight_s = lightPos.x;
						surf.dlight_t = lightPos.y;
						
						base_Renamed = gl_lms.lightmap_buffer;
						base_Renamed.position(surf.dlight_t * BLOCK_WIDTH + surf.dlight_s);
						
						R_BuildLightMap(surf, base_Renamed.slice(), BLOCK_WIDTH);
					}
				}
				
				/*
				** draw remainder of dynamic lightmaps that haven't been uploaded yet
				*/
				if (newdrawsurf != null)
					LM_UploadBlock(true);
				
				for (surf = newdrawsurf; surf != null; surf = surf.lightmapchain)
				{
					if (surf.polys != null)
						DrawGLPolyChain(surf.polys, (surf.light_s - surf.dlight_s) * (1.0f / 128.0f), (surf.light_t - surf.dlight_t) * (1.0f / 128.0f));
				}
			}
			
			/*
			** restore state
			*/
			gl.glDisable(net.java.games.jogl.GL_Fields.GL_BLEND);
			gl.glBlendFunc(net.java.games.jogl.GL_Fields.GL_SRC_ALPHA, net.java.games.jogl.GL_Fields.GL_ONE_MINUS_SRC_ALPHA);
			gl.glDepthMask(true);
		}
		
		//UPGRADE_NOTE: The initialization of  'temp2' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		private IntBuffer temp2;
		
		/*
		================
		R_RenderBrushPoly
		================
		*/
		internal virtual void  R_RenderBrushPoly(msurface_t fa)
		{
			int maps;
			image_t image;
			bool is_dynamic = false;
			
			c_brush_polys++;
			
			image = R_TextureAnimation(fa.texinfo);
			
			if ((fa.flags & Defines.SURF_DRAWTURB) != 0)
			{
				GL_Bind(image.texnum);
				
				// warp texture, no lightmaps
				GL_TexEnv(net.java.games.jogl.GL_Fields.GL_MODULATE);
				gl.glColor4f(gl_state.inverse_intensity, gl_state.inverse_intensity, gl_state.inverse_intensity, 1.0F);
				EmitWaterPolys(fa);
				GL_TexEnv(net.java.games.jogl.GL_Fields.GL_REPLACE);
				
				return ;
			}
			else
			{
				GL_Bind(image.texnum);
				GL_TexEnv(net.java.games.jogl.GL_Fields.GL_REPLACE);
			}
			
			//	  ======
			//	  PGM
			if ((fa.texinfo.flags & Defines.SURF_FLOWING) != 0)
				DrawGLFlowingPoly(fa);
			else
				DrawGLPoly(fa.polys);
			//	  PGM
			//	  ======
			
			// ersetzt goto
			bool gotoDynamic = false;
			/*
			** check for lightmap modification
			*/
			for (maps = 0; maps < Defines.MAXLIGHTMAPS && fa.styles[maps] != (sbyte) SupportClass.Identity(255); maps++)
			{
				if (r_newrefdef.lightstyles[fa.styles[maps] & 0xFF].white != fa.cached_light[maps])
				{
					gotoDynamic = true;
					break;
				}
			}
			
			// this is a hack from cwei
			if (maps == 4)
				maps--;
			
			// dynamic this frame or dynamic previously
			if (gotoDynamic || (fa.dlightframe == r_framecount))
			{
				//	label dynamic:
				if (gl_dynamic.value_Renamed != 0)
				{
					if ((fa.texinfo.flags & (Defines.SURF_SKY | Defines.SURF_TRANS33 | Defines.SURF_TRANS66 | Defines.SURF_WARP)) == 0)
					{
						is_dynamic = true;
					}
				}
			}
			
			if (is_dynamic)
			{
				if (((fa.styles[maps] & 0xFF) >= 32 || fa.styles[maps] == 0) && (fa.dlightframe != r_framecount))
				{
					// ist ersetzt durch temp2:	unsigned	temp[34*34];
					int smax, tmax;
					
					smax = (fa.extents[0] >> 4) + 1;
					tmax = (fa.extents[1] >> 4) + 1;
					
					R_BuildLightMap(fa, temp2, smax);
					R_SetCacheState(fa);
					
					GL_Bind(gl_state.lightmap_textures + fa.lightmaptexturenum);
					
					gl.glTexSubImage2D(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D, 0, fa.light_s, fa.light_t, smax, tmax, GL_LIGHTMAP_FORMAT, net.java.games.jogl.GL_Fields.GL_UNSIGNED_BYTE, temp2);
					
					fa.lightmapchain = gl_lms.lightmap_surfaces[fa.lightmaptexturenum];
					gl_lms.lightmap_surfaces[fa.lightmaptexturenum] = fa;
				}
				else
				{
					fa.lightmapchain = gl_lms.lightmap_surfaces[0];
					gl_lms.lightmap_surfaces[0] = fa;
				}
			}
			else
			{
				fa.lightmapchain = gl_lms.lightmap_surfaces[fa.lightmaptexturenum];
				gl_lms.lightmap_surfaces[fa.lightmaptexturenum] = fa;
			}
		}
		
		
		/*
		================
		R_DrawAlphaSurfaces
		
		Draw water surfaces and windows.
		The BSP tree is waled front to back, so unwinding the chain
		of alpha_surfaces will draw back to front, giving proper ordering.
		================
		*/
		internal override void  R_DrawAlphaSurfaces()
		{
			msurface_t s;
			float intens;
			
			//
			// go back to the world matrix
			//
			gl.glLoadMatrixf(r_world_matrix);
			
			gl.glEnable(net.java.games.jogl.GL_Fields.GL_BLEND);
			GL_TexEnv(net.java.games.jogl.GL_Fields.GL_MODULATE);
			
			// the textures are prescaled up for a better lighting range,
			// so scale it back down
			intens = gl_state.inverse_intensity;
			
			for (s = r_alpha_surfaces; s != null; s = s.texturechain)
			{
				GL_Bind(s.texinfo.image.texnum);
				c_brush_polys++;
				if ((s.texinfo.flags & Defines.SURF_TRANS33) != 0)
					gl.glColor4f(intens, intens, intens, 0.33f);
				else if ((s.texinfo.flags & Defines.SURF_TRANS66) != 0)
					gl.glColor4f(intens, intens, intens, 0.66f);
				else
					gl.glColor4f(intens, intens, intens, 1);
				if ((s.flags & Defines.SURF_DRAWTURB) != 0)
					EmitWaterPolys(s);
				else if ((s.texinfo.flags & Defines.SURF_FLOWING) != 0)
				// PGM	9/16/98
					DrawGLFlowingPoly(s);
				// PGM
				else
					DrawGLPoly(s.polys);
			}
			
			GL_TexEnv(net.java.games.jogl.GL_Fields.GL_REPLACE);
			gl.glColor4f(1, 1, 1, 1);
			gl.glDisable(net.java.games.jogl.GL_Fields.GL_BLEND);
			
			r_alpha_surfaces = null;
		}
		
		/*
		================
		DrawTextureChains
		================
		*/
		internal virtual void  DrawTextureChains()
		{
			int i;
			msurface_t s;
			image_t image;
			
			c_visible_textures = 0;
			
			
			if (!qglSelectTextureSGIS && !qglActiveTextureARB)
			{
				for (i = 0; i < numgltextures; i++)
				{
					image = gltextures[i];
					if (image.registration_sequence == 0)
						continue;
					s = image.texturechain;
					if (s == null)
						continue;
					c_visible_textures++;
					
					for (; s != null; s = s.texturechain)
						R_RenderBrushPoly(s);
					
					image.texturechain = null;
				}
			}
			else
			{
				for (i = 0; i < numgltextures; i++)
				{
					image = gltextures[i];
					
					if (image.registration_sequence == 0)
						continue;
					if (image.texturechain == null)
						continue;
					c_visible_textures++;
					
					for (s = image.texturechain; s != null; s = s.texturechain)
					{
						if ((s.flags & Defines.SURF_DRAWTURB) == 0)
							R_RenderBrushPoly(s);
					}
				}
				
				GL_EnableMultitexture(false);
				for (i = 0; i < numgltextures; i++)
				{
					image = gltextures[i];
					
					if (image.registration_sequence == 0)
						continue;
					s = image.texturechain;
					if (s == null)
						continue;
					
					for (; s != null; s = s.texturechain)
					{
						if ((s.flags & Defines.SURF_DRAWTURB) != 0)
							R_RenderBrushPoly(s);
					}
					
					image.texturechain = null;
				}
			}
			
			GL_TexEnv(net.java.games.jogl.GL_Fields.GL_REPLACE);
		}
		
		// direct buffer
		//UPGRADE_NOTE: The initialization of  'temp' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		private IntBuffer temp;
		
		internal virtual void  GL_RenderLightmappedPoly(msurface_t surf)
		{
			int i, nv = surf.polys.numverts;
			int map = 0;
			float[] v;
			image_t image = R_TextureAnimation(surf.texinfo);
			bool is_dynamic = false;
			int lmtex = surf.lightmaptexturenum;
			glpoly_t p;
			
			// ersetzt goto
			bool gotoDynamic = false;
			
			for (map = 0; map < Defines.MAXLIGHTMAPS && (surf.styles[map] != (sbyte) SupportClass.Identity(255)); map++)
			{
				if (r_newrefdef.lightstyles[surf.styles[map] & 0xFF].white != surf.cached_light[map])
				{
					gotoDynamic = true;
					break;
				}
			}
			
			// this is a hack from cwei
			if (map == 4)
				map--;
			
			// dynamic this frame or dynamic previously
			if (gotoDynamic || (surf.dlightframe == r_framecount))
			{
				//	label dynamic:
				if (gl_dynamic.value_Renamed != 0)
				{
					if ((surf.texinfo.flags & (Defines.SURF_SKY | Defines.SURF_TRANS33 | Defines.SURF_TRANS66 | Defines.SURF_WARP)) == 0)
					{
						is_dynamic = true;
					}
				}
			}
			
			if (is_dynamic)
			{
				// ist raus gezogen worden int[] temp = new int[128*128];
				int smax, tmax;
				
				if (((surf.styles[map] & 0xFF) >= 32 || surf.styles[map] == 0) && (surf.dlightframe != r_framecount))
				{
					smax = (surf.extents[0] >> 4) + 1;
					tmax = (surf.extents[1] >> 4) + 1;
					
					R_BuildLightMap(surf, temp, smax);
					R_SetCacheState(surf);
					
					GL_MBind(GL_TEXTURE1, gl_state.lightmap_textures + surf.lightmaptexturenum);
					
					lmtex = surf.lightmaptexturenum;
					
					gl.glTexSubImage2D(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D, 0, surf.light_s, surf.light_t, smax, tmax, GL_LIGHTMAP_FORMAT, net.java.games.jogl.GL_Fields.GL_UNSIGNED_BYTE, temp);
				}
				else
				{
					smax = (surf.extents[0] >> 4) + 1;
					tmax = (surf.extents[1] >> 4) + 1;
					
					R_BuildLightMap(surf, temp, smax);
					
					GL_MBind(GL_TEXTURE1, gl_state.lightmap_textures + 0);
					
					lmtex = 0;
					
					gl.glTexSubImage2D(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D, 0, surf.light_s, surf.light_t, smax, tmax, GL_LIGHTMAP_FORMAT, net.java.games.jogl.GL_Fields.GL_UNSIGNED_BYTE, temp);
				}
				
				c_brush_polys++;
				
				GL_MBind(GL_TEXTURE0, image.texnum);
				GL_MBind(GL_TEXTURE1, gl_state.lightmap_textures + lmtex);
				
				// ==========
				//	  PGM
				if ((surf.texinfo.flags & Defines.SURF_FLOWING) != 0)
				{
					float scroll;
					
					//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
					scroll = (- 64) * ((r_newrefdef.time / 40.0f) - (int) (r_newrefdef.time / 40.0f));
					if (scroll == 0.0f)
						scroll = - 64.0f;
					
					for (p = surf.polys; p != null; p = p.chain)
					{
						gl.glBegin(net.java.games.jogl.GL_Fields.GL_POLYGON);
						for (i = 0; i < nv; i++)
						{
							gl.glMultiTexCoord2fARB(GL_TEXTURE0, p.s1(i) + scroll, p.t1(i));
							gl.glMultiTexCoord2fARB(GL_TEXTURE1, p.s2(i), p.t2(i));
							//gglMTexCoord2fSGIS( GL_TEXTURE0, v[3] + scroll, v[4]);
							//gglMTexCoord2fSGIS( GL_TEXTURE1, v[5], v[6]);
							gl.glVertex3f(p.x(i), p.y(i), p.z(i));
						}
						gl.glEnd();
					}
				}
				else
				{
					for (p = surf.polys; p != null; p = p.chain)
					{
						gl.glBegin(net.java.games.jogl.GL_Fields.GL_POLYGON);
						for (i = 0; i < nv; i++)
						{
							gl.glMultiTexCoord2fARB(GL_TEXTURE0, p.s1(i), p.t1(i));
							gl.glMultiTexCoord2fARB(GL_TEXTURE1, p.s2(i), p.t2(i));
							//gglMTexCoord2fSGIS( GL_TEXTURE0, v[3], v[4]);
							//gglMTexCoord2fSGIS( GL_TEXTURE1, v[5], v[6]);
							gl.glVertex3f(p.x(i), p.y(i), p.z(i));
						}
						gl.glEnd();
					}
				}
				// PGM
				// ==========
			}
			else
			{
				c_brush_polys++;
				
				GL_MBind(GL_TEXTURE0, image.texnum);
				GL_MBind(GL_TEXTURE1, gl_state.lightmap_textures + lmtex);
				
				// ==========
				//	  PGM
				if ((surf.texinfo.flags & Defines.SURF_FLOWING) != 0)
				{
					float scroll;
					
					//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
					scroll = (- 64) * ((r_newrefdef.time / 40.0f) - (int) (r_newrefdef.time / 40.0f));
					if (scroll == 0.0)
						scroll = - 64.0f;
					
					for (p = surf.polys; p != null; p = p.chain)
					{
						gl.glBegin(net.java.games.jogl.GL_Fields.GL_POLYGON);
						for (i = 0; i < nv; i++)
						{
							gl.glMultiTexCoord2fARB(GL_TEXTURE0, p.s1(i) + scroll, p.t1(i));
							gl.glMultiTexCoord2fARB(GL_TEXTURE1, p.s2(i), p.t2(i));
							//gglMTexCoord2fSGIS( GL_TEXTURE0, v[3] + scroll, v[4]);
							//gglMTexCoord2fSGIS( GL_TEXTURE1, v[5], v[6]);
							gl.glVertex3f(p.x(i), p.y(i), p.z(i));
						}
						gl.glEnd();
					}
				}
				else
				{
					// PGM
					//  ==========
					for (p = surf.polys; p != null; p = p.chain)
					{
						gl.glBegin(net.java.games.jogl.GL_Fields.GL_POLYGON);
						for (i = 0; i < nv; i++)
						{
							gl.glMultiTexCoord2fARB(GL_TEXTURE0, p.s1(i), p.t1(i));
							gl.glMultiTexCoord2fARB(GL_TEXTURE1, p.s2(i), p.t2(i));
							//gglMTexCoord2fSGIS( GL_TEXTURE0, v[3], v[4]);
							//gglMTexCoord2fSGIS( GL_TEXTURE1, v[5], v[6]);
							gl.glVertex3f(p.x(i), p.y(i), p.z(i));
						}
						gl.glEnd();
					}
					// ==========
					// PGM
				}
				// PGM
				// ==========
			}
		}
		
		/*
		=================
		R_DrawInlineBModel
		=================
		*/
		internal virtual void  R_DrawInlineBModel()
		{
			int i, k;
			cplane_t pplane;
			float dot;
			msurface_t psurf;
			dlight_t lt;
			
			// calculate dynamic lighting for bmodel
			if (gl_flashblend.value_Renamed == 0)
			{
				for (k = 0; k < r_newrefdef.num_dlights; k++)
				{
					lt = r_newrefdef.dlights[k];
					R_MarkLights(lt, 1 << k, currentmodel.nodes[currentmodel.firstnode]);
				}
			}
			
			// psurf = &currentmodel->surfaces[currentmodel->firstmodelsurface];
			int psurfp = currentmodel.firstmodelsurface;
			msurface_t[] surfaces;
			surfaces = currentmodel.surfaces;
			//psurf = surfaces[psurfp];
			
			if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0)
			{
				gl.glEnable(net.java.games.jogl.GL_Fields.GL_BLEND);
				gl.glColor4f(1, 1, 1, 0.25f);
				GL_TexEnv(net.java.games.jogl.GL_Fields.GL_MODULATE);
			}
			
			//
			// draw texture
			//
			for (i = 0; i < currentmodel.nummodelsurfaces; i++)
			{
				psurf = surfaces[psurfp++];
				// find which side of the node we are on
				pplane = psurf.plane;
				
				dot = Math3D.DotProduct(modelorg, pplane.normal) - pplane.dist;
				
				// draw the polygon
				if (((psurf.flags & Defines.SURF_PLANEBACK) != 0 && (dot < - BACKFACE_EPSILON)) || ((psurf.flags & Defines.SURF_PLANEBACK) == 0 && (dot > BACKFACE_EPSILON)))
				{
					if ((psurf.texinfo.flags & (Defines.SURF_TRANS33 | Defines.SURF_TRANS66)) != 0)
					{
						// add to the translucent chain
						psurf.texturechain = r_alpha_surfaces;
						r_alpha_surfaces = psurf;
					}
					else if (qglMTexCoord2fSGIS && (psurf.flags & Defines.SURF_DRAWTURB) == 0)
					{
						GL_RenderLightmappedPoly(psurf);
					}
					else
					{
						GL_EnableMultitexture(false);
						R_RenderBrushPoly(psurf);
						GL_EnableMultitexture(true);
					}
				}
			}
			
			if ((currententity.flags & Defines.RF_TRANSLUCENT) == 0)
			{
				if (!qglMTexCoord2fSGIS)
					R_BlendLightmaps();
			}
			else
			{
				gl.glDisable(net.java.games.jogl.GL_Fields.GL_BLEND);
				gl.glColor4f(1, 1, 1, 1);
				GL_TexEnv(net.java.games.jogl.GL_Fields.GL_REPLACE);
			}
		}
		
		/*
		=================
		R_DrawBrushModel
		=================
		*/
		internal override void  R_DrawBrushModel(entity_t e)
		{
			float[] mins = new float[]{0, 0, 0};
			float[] maxs = new float[]{0, 0, 0};
			int i;
			bool rotated;
			
			if (currentmodel.nummodelsurfaces == 0)
				return ;
			
			currententity = e;
			gl_state.currenttextures[0] = gl_state.currenttextures[1] = - 1;
			
			if (e.angles[0] != 0 || e.angles[1] != 0 || e.angles[2] != 0)
			{
				rotated = true;
				for (i = 0; i < 3; i++)
				{
					mins[i] = e.origin[i] - currentmodel.radius;
					maxs[i] = e.origin[i] + currentmodel.radius;
					//System.out.println("rotate: " + Lib.vtos(mins) + " " + Lib.vtos(maxs));
				}
			}
			else
			{
				rotated = false;
				Math3D.VectorAdd(e.origin, currentmodel.mins, mins);
				Math3D.VectorAdd(e.origin, currentmodel.maxs, maxs);
				//System.out.println("       "  + Lib.vtos(mins) + " " + Lib.vtos(maxs));
			}
			
			if (R_CullBox(mins, maxs))
			{
				//System.out.println("origin "  + Lib.vtos(e.origin) + " +++ " + Lib.vtos(currentmodel.mins));
				return ;
			}
			
			gl.glColor3f(1, 1, 1);
			
			// memset (gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces));
			gl_lms.clearLightmapSurfaces();
			
			Math3D.VectorSubtract(r_newrefdef.vieworg, e.origin, modelorg);
			if (rotated)
			{
				float[] temp = new float[]{0, 0, 0};
				float[] forward = new float[]{0, 0, 0};
				float[] right = new float[]{0, 0, 0};
				float[] up = new float[]{0, 0, 0};
				
				Math3D.VectorCopy(modelorg, temp);
				Math3D.AngleVectors(e.angles, forward, right, up);
				modelorg[0] = Math3D.DotProduct(temp, forward);
				modelorg[1] = - Math3D.DotProduct(temp, right);
				modelorg[2] = Math3D.DotProduct(temp, up);
			}
			
			gl.glPushMatrix();
			
			e.angles[0] = - e.angles[0]; // stupid quake bug
			e.angles[2] = - e.angles[2]; // stupid quake bug
			R_RotateForEntity(e);
			e.angles[0] = - e.angles[0]; // stupid quake bug
			e.angles[2] = - e.angles[2]; // stupid quake bug
			
			GL_EnableMultitexture(true);
			GL_SelectTexture(GL_TEXTURE0);
			GL_TexEnv(net.java.games.jogl.GL_Fields.GL_REPLACE);
			GL_SelectTexture(GL_TEXTURE1);
			GL_TexEnv(net.java.games.jogl.GL_Fields.GL_MODULATE);
			
			R_DrawInlineBModel();
			GL_EnableMultitexture(false);
			
			gl.glPopMatrix();
		}
		
		/*
		=============================================================
		
		WORLD MODEL
		
		=============================================================
		*/
		
		/*
		================
		R_RecursiveWorldNode
		================
		*/
		internal virtual void  R_RecursiveWorldNode(mnode_t node)
		{
			int c, side, sidebit;
			cplane_t plane;
			msurface_t surf;
			msurface_t mark;
			mleaf_t pleaf;
			float dot = 0;
			image_t image;
			
			if (node.contents == Defines.CONTENTS_SOLID)
				return ; // solid
			
			if (node.visframe != r_visframecount)
				return ;
			
			if (R_CullBox(node.mins, node.maxs))
				return ;
			
			// if a leaf node, draw stuff
			if (node.contents != - 1)
			{
				pleaf = (mleaf_t) node;
				
				// check for door connected areas
				if (r_newrefdef.areabits != null)
				{
					if (((r_newrefdef.areabits[pleaf.area >> 3] & 0xFF) & (1 << (pleaf.area & 7))) == 0)
						return ; // not visible
				}
				
				int markp = 0;
				
				mark = pleaf.getMarkSurface(markp); // first marked surface
				c = pleaf.nummarksurfaces;
				
				if (c != 0)
				{
					do 
					{
						mark.visframe = r_framecount;
						mark = pleaf.getMarkSurface(++markp); // next surface
					}
					while (--c != 0);
				}
				
				return ;
			}
			
			// node is just a decision point, so go down the apropriate sides
			
			// find which side of the node we are on
			plane = node.plane;
			
			switch (plane.type)
			{
				
				case (sbyte) (Defines.PLANE_X): 
					dot = modelorg[0] - plane.dist;
					break;
				
				case (sbyte) (Defines.PLANE_Y): 
					dot = modelorg[1] - plane.dist;
					break;
				
				case (sbyte) (Defines.PLANE_Z): 
					dot = modelorg[2] - plane.dist;
					break;
				
				default: 
					dot = Math3D.DotProduct(modelorg, plane.normal) - plane.dist;
					break;
				
			}
			
			if (dot >= 0.0f)
			{
				side = 0;
				sidebit = 0;
			}
			else
			{
				side = 1;
				sidebit = Defines.SURF_PLANEBACK;
			}
			
			// recurse down the children, front side first
			R_RecursiveWorldNode(node.children[side]);
			
			// draw stuff
			//for ( c = node.numsurfaces, surf = r_worldmodel.surfaces[node.firstsurface]; c != 0 ; c--, surf++)
			for (c = 0; c < node.numsurfaces; c++)
			{
				surf = r_worldmodel.surfaces[node.firstsurface + c];
				if (surf.visframe != r_framecount)
					continue;
				
				if ((surf.flags & Defines.SURF_PLANEBACK) != sidebit)
					continue; // wrong side
				
				if ((surf.texinfo.flags & Defines.SURF_SKY) != 0)
				{
					// just adds to visible sky bounds
					R_AddSkySurface(surf);
				}
				else if ((surf.texinfo.flags & (Defines.SURF_TRANS33 | Defines.SURF_TRANS66)) != 0)
				{
					// add to the translucent chain
					surf.texturechain = r_alpha_surfaces;
					r_alpha_surfaces = surf;
				}
				else
				{
					if (qglMTexCoord2fSGIS && (surf.flags & Defines.SURF_DRAWTURB) == 0)
					{
						GL_RenderLightmappedPoly(surf);
					}
					else
					{
						// the polygon is visible, so add it to the texture
						// sorted chain
						// FIXME: this is a hack for animation
						image = R_TextureAnimation(surf.texinfo);
						surf.texturechain = image.texturechain;
						image.texturechain = surf;
					}
				}
			}
			
			// recurse down the back side
			R_RecursiveWorldNode(node.children[1 - side]);
		}
		
		
		/*
		=============
		R_DrawWorld
		=============
		*/
		internal override void  R_DrawWorld()
		{
			entity_t ent = new entity_t();
			// auto cycle the world frame for texture animation
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			ent.frame = (int) (r_newrefdef.time * 2);
			currententity = ent;
			
			if (r_drawworld.value_Renamed == 0)
				return ;
			
			if ((r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) != 0)
				return ;
			
			currentmodel = r_worldmodel;
			
			Math3D.VectorCopy(r_newrefdef.vieworg, modelorg);
			
			gl_state.currenttextures[0] = gl_state.currenttextures[1] = - 1;
			
			gl.glColor3f(1, 1, 1);
			// memset (gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces));
			gl_lms.clearLightmapSurfaces();
			
			R_ClearSkyBox();
			
			if (qglMTexCoord2fSGIS)
			{
				GL_EnableMultitexture(true);
				
				GL_SelectTexture(GL_TEXTURE0);
				GL_TexEnv(net.java.games.jogl.GL_Fields.GL_REPLACE);
				GL_SelectTexture(GL_TEXTURE1);
				
				if (gl_lightmap.value_Renamed != 0)
					GL_TexEnv(net.java.games.jogl.GL_Fields.GL_REPLACE);
				else
					GL_TexEnv(net.java.games.jogl.GL_Fields.GL_MODULATE);
				
				R_RecursiveWorldNode(r_worldmodel.nodes[0]); // root node
				
				GL_EnableMultitexture(false);
			}
			else
			{
				R_RecursiveWorldNode(r_worldmodel.nodes[0]); // root node
			}
			
			/*
			** theoretically nothing should happen in the next two functions
			** if multitexture is enabled
			*/
			DrawTextureChains();
			R_BlendLightmaps();
			
			R_DrawSkyBox();
			
			R_DrawTriangleOutlines();
		}
		
		//UPGRADE_NOTE: The initialization of  'fatvis' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal sbyte[] fatvis;
		
		/*
		===============
		R_MarkLeaves
		
		Mark the leaves and nodes that are in the PVS for the current
		cluster
		===============
		*/
		internal override void  R_MarkLeaves()
		{
			sbyte[] vis;
			//byte[] fatvis = new byte[Defines.MAX_MAP_LEAFS / 8];
			
			SupportClass.ArraySupport.Fill(SupportClass.ToByteArray(fatvis), (byte) 0);
			
			mnode_t node;
			int i, c;
			mleaf_t leaf;
			int cluster;
			
			if (r_oldviewcluster == r_viewcluster && r_oldviewcluster2 == r_viewcluster2 && r_novis.value_Renamed == 0 && r_viewcluster != - 1)
				return ;
			
			// development aid to let you run around and see exactly where
			// the pvs ends
			if (gl_lockpvs.value_Renamed != 0)
				return ;
			
			r_visframecount++;
			r_oldviewcluster = r_viewcluster;
			r_oldviewcluster2 = r_viewcluster2;
			
			if (r_novis.value_Renamed != 0 || r_viewcluster == - 1 || r_worldmodel.vis == null)
			{
				// mark everything
				for (i = 0; i < r_worldmodel.numleafs; i++)
					r_worldmodel.leafs[i].visframe = r_visframecount;
				for (i = 0; i < r_worldmodel.numnodes; i++)
					r_worldmodel.nodes[i].visframe = r_visframecount;
				return ;
			}
			
			vis = Mod_ClusterPVS(r_viewcluster, r_worldmodel);
			// may have to combine two clusters because of solid water boundaries
			if (r_viewcluster2 != r_viewcluster)
			{
				// memcpy (fatvis, vis, (r_worldmodel.numleafs+7)/8);
				Array.Copy(vis, 0, fatvis, 0, (r_worldmodel.numleafs + 7) / 8);
				vis = Mod_ClusterPVS(r_viewcluster2, r_worldmodel);
				c = (r_worldmodel.numleafs + 31) / 32;
				int k = 0;
				for (i = 0; i < c; i++)
				{
					fatvis[k] |= vis[k++];
					fatvis[k] |= vis[k++];
					fatvis[k] |= vis[k++];
					fatvis[k] |= vis[k++];
				}
				
				vis = fatvis;
			}
			
			for (i = 0; i < r_worldmodel.numleafs; i++)
			{
				leaf = r_worldmodel.leafs[i];
				cluster = leaf.cluster;
				if (cluster == - 1)
					continue;
				if (((vis[cluster >> 3] & 0xFF) & (1 << (cluster & 7))) != 0)
				{
					node = (mnode_t) leaf;
					do 
					{
						if (node.visframe == r_visframecount)
							break;
						node.visframe = r_visframecount;
						node = node.parent;
					}
					while (node != null);
				}
			}
		}
		
		
		
		/*
		=============================================================================
		
		LIGHTMAP ALLOCATION
		
		=============================================================================
		*/
		
		internal virtual void  LM_InitBlock()
		{
			SupportClass.ArraySupport.Fill(gl_lms.allocated, 0);
		}
		
		internal virtual void  LM_UploadBlock(bool dynamic)
		{
			int texture;
			int height = 0;
			
			if (dynamic)
			{
				texture = 0;
			}
			else
			{
				texture = gl_lms.current_lightmap_texture;
			}
			
			GL_Bind(gl_state.lightmap_textures + texture);
			gl.glTexParameterf(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D, net.java.games.jogl.GL_Fields.GL_TEXTURE_MIN_FILTER, net.java.games.jogl.GL_Fields.GL_LINEAR);
			gl.glTexParameterf(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D, net.java.games.jogl.GL_Fields.GL_TEXTURE_MAG_FILTER, net.java.games.jogl.GL_Fields.GL_LINEAR);
			
			if (dynamic)
			{
				int i;
				
				for (i = 0; i < BLOCK_WIDTH; i++)
				{
					if (gl_lms.allocated[i] > height)
						height = gl_lms.allocated[i];
				}
				
				gl.glTexSubImage2D(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D, 0, 0, 0, BLOCK_WIDTH, height, GL_LIGHTMAP_FORMAT, net.java.games.jogl.GL_Fields.GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer);
			}
			else
			{
				gl.glTexImage2D(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D, 0, gl_lms.internal_format, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, net.java.games.jogl.GL_Fields.GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer);
				if (++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS)
					Com.Error(Defines.ERR_DROP, "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n");
				
				
				//debugLightmap(gl_lms.lightmap_buffer, 128, 128, 4);
			}
		}
		
		// returns a texture number and the position inside it
		internal virtual bool LM_AllocBlock(int w, int h, pos_t pos)
		{
			int x = pos.x;
			int y = pos.y;
			int i, j;
			int best, best2;
			
			best = BLOCK_HEIGHT;
			
			for (i = 0; i < BLOCK_WIDTH - w; i++)
			{
				best2 = 0;
				
				for (j = 0; j < w; j++)
				{
					if (gl_lms.allocated[i + j] >= best)
						break;
					if (gl_lms.allocated[i + j] > best2)
						best2 = gl_lms.allocated[i + j];
				}
				if (j == w)
				{
					// this is a valid spot
					pos.x = x = i;
					pos.y = y = best = best2;
				}
			}
			
			if (best + h > BLOCK_HEIGHT)
				return false;
			
			for (i = 0; i < w; i++)
				gl_lms.allocated[x + i] = best + h;
			
			return true;
		}
		
		/*
		================
		GL_BuildPolygonFromSurface
		================
		*/
		internal virtual void  GL_BuildPolygonFromSurface(msurface_t fa)
		{
			int i, lindex, lnumverts;
			medge_t[] pedges;
			medge_t r_pedge;
			int vertpage;
			float[] vec;
			float s, t;
			glpoly_t poly;
			float[] total = new float[]{0, 0, 0};
			
			// reconstruct the polygon
			pedges = currentmodel.edges;
			lnumverts = fa.numedges;
			vertpage = 0;
			
			Math3D.VectorClear(total);
			//
			// draw texture
			//
			// poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float));
			poly = Polygon.create(lnumverts);
			
			poly.next = fa.polys;
			poly.flags = fa.flags;
			fa.polys = poly;
			
			for (i = 0; i < lnumverts; i++)
			{
				lindex = currentmodel.surfedges[fa.firstedge + i];
				
				if (lindex > 0)
				{
					r_pedge = pedges[lindex];
					vec = currentmodel.vertexes[r_pedge.v[0]].position;
				}
				else
				{
					r_pedge = pedges[- lindex];
					vec = currentmodel.vertexes[r_pedge.v[1]].position;
				}
				s = Math3D.DotProduct(vec, fa.texinfo.vecs[0]) + fa.texinfo.vecs[0][3];
				s /= fa.texinfo.image.width;
				
				t = Math3D.DotProduct(vec, fa.texinfo.vecs[1]) + fa.texinfo.vecs[1][3];
				t /= fa.texinfo.image.height;
				
				Math3D.VectorAdd(total, vec, total);
				//Math3D.VectorCopy (vec, poly.verts[i]);
				poly.x(i, vec[0]);
				poly.y(i, vec[1]);
				poly.z(i, vec[2]);
				
				//poly.verts[i][3] = s;
				//poly.verts[i][4] = t;
				poly.s1(i, s);
				poly.t1(i, t);
				
				//
				// lightmap texture coordinates
				//
				s = Math3D.DotProduct(vec, fa.texinfo.vecs[0]) + fa.texinfo.vecs[0][3];
				s -= fa.texturemins[0];
				s += fa.light_s * 16;
				s += 8;
				s /= BLOCK_WIDTH * 16; //fa.texinfo.texture.width;
				
				t = Math3D.DotProduct(vec, fa.texinfo.vecs[1]) + fa.texinfo.vecs[1][3];
				t -= fa.texturemins[1];
				t += fa.light_t * 16;
				t += 8;
				t /= BLOCK_HEIGHT * 16; //fa.texinfo.texture.height;
				
				//poly.verts[i][5] = s;
				//poly.verts[i][6] = t;
				poly.s2(i, s);
				poly.t2(i, t);
			}
		}
		
		/*
		========================
		GL_CreateSurfaceLightmap
		========================
		*/
		internal virtual void  GL_CreateSurfaceLightmap(msurface_t surf)
		{
			int smax, tmax;
			IntBuffer base_Renamed;
			
			if ((surf.flags & (Defines.SURF_DRAWSKY | Defines.SURF_DRAWTURB)) != 0)
				return ;
			
			smax = (surf.extents[0] >> 4) + 1;
			tmax = (surf.extents[1] >> 4) + 1;
			
			pos_t lightPos = new pos_t(surf.light_s, surf.light_t);
			
			if (!LM_AllocBlock(smax, tmax, lightPos))
			{
				LM_UploadBlock(false);
				LM_InitBlock();
				lightPos = new pos_t(surf.light_s, surf.light_t);
				if (!LM_AllocBlock(smax, tmax, lightPos))
				{
					Com.Error(Defines.ERR_FATAL, "Consecutive calls to LM_AllocBlock(" + smax + "," + tmax + ") failed\n");
				}
			}
			
			// kopiere die koordinaten zurueck
			surf.light_s = lightPos.x;
			surf.light_t = lightPos.y;
			
			surf.lightmaptexturenum = gl_lms.current_lightmap_texture;
			
			int basep = (surf.light_t * BLOCK_WIDTH + surf.light_s); // * LIGHTMAP_BYTES;
			base_Renamed = gl_lms.lightmap_buffer;
			base_Renamed.position(basep);
			
			R_SetCacheState(surf);
			R_BuildLightMap(surf, base_Renamed.slice(), BLOCK_WIDTH);
		}
		
		internal lightstyle_t[] lightstyles;
		
		/*
		==================
		GL_BeginBuildingLightmaps
		
		==================
		*/
		internal virtual void  GL_BeginBuildingLightmaps(model_t m)
		{
			// static lightstyle_t	lightstyles[MAX_LIGHTSTYLES];
			int i;
			int[] dummy = new int[128 * 128];
			
			// init lightstyles
			if (lightstyles == null)
			{
				lightstyles = new lightstyle_t[Defines.MAX_LIGHTSTYLES];
				for (i = 0; i < lightstyles.Length; i++)
				{
					lightstyles[i] = new lightstyle_t();
				}
			}
			
			// memset( gl_lms.allocated, 0, sizeof(gl_lms.allocated) );
			SupportClass.ArraySupport.Fill(gl_lms.allocated, 0);
			
			r_framecount = 1; // no dlightcache
			
			GL_EnableMultitexture(true);
			GL_SelectTexture(GL_TEXTURE1);
			
			/*
			** setup the base lightstyles so the lightmaps won't have to be regenerated
			** the first time they're seen
			*/
			for (i = 0; i < Defines.MAX_LIGHTSTYLES; i++)
			{
				lightstyles[i].rgb[0] = 1;
				lightstyles[i].rgb[1] = 1;
				lightstyles[i].rgb[2] = 1;
				lightstyles[i].white = 3;
			}
			r_newrefdef.lightstyles = lightstyles;
			
			if (gl_state.lightmap_textures == 0)
			{
				gl_state.lightmap_textures = TEXNUM_LIGHTMAPS;
			}
			
			gl_lms.current_lightmap_texture = 1;
			
			/*
			** if mono lightmaps are enabled and we want to use alpha
			** blending (a,1-a) then we're likely running on a 3DLabs
			** Permedia2.  In a perfect world we'd use a GL_ALPHA lightmap
			** in order to conserve space and maximize bandwidth, however 
			** this isn't a perfect world.
			**
			** So we have to use alpha lightmaps, but stored in GL_RGBA format,
			** which means we only get 1/16th the color resolution we should when
			** using alpha lightmaps.  If we find another board that supports
			** only alpha lightmaps but that can at least support the GL_ALPHA
			** format then we should change this code to use real alpha maps.
			*/
			
			char format = gl_monolightmap.string_Renamed.ToUpper()[0];
			
			if (format == 'A')
			{
				gl_lms.internal_format = gl_tex_alpha_format;
			}
			/*
			** try to do hacked colored lighting with a blended texture
			*/
			else if (format == 'C')
			{
				gl_lms.internal_format = gl_tex_alpha_format;
			}
			else if (format == 'I')
			{
				gl_lms.internal_format = net.java.games.jogl.GL_Fields.GL_INTENSITY8;
			}
			else if (format == 'L')
			{
				gl_lms.internal_format = net.java.games.jogl.GL_Fields.GL_LUMINANCE8;
			}
			else
			{
				gl_lms.internal_format = gl_tex_solid_format;
			}
			
			/*
			** initialize the dynamic lightmap texture
			*/
			GL_Bind(gl_state.lightmap_textures + 0);
			gl.glTexParameterf(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D, net.java.games.jogl.GL_Fields.GL_TEXTURE_MIN_FILTER, net.java.games.jogl.GL_Fields.GL_LINEAR);
			gl.glTexParameterf(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D, net.java.games.jogl.GL_Fields.GL_TEXTURE_MAG_FILTER, net.java.games.jogl.GL_Fields.GL_LINEAR);
			gl.glTexImage2D(net.java.games.jogl.GL_Fields.GL_TEXTURE_2D, 0, gl_lms.internal_format, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, net.java.games.jogl.GL_Fields.GL_UNSIGNED_BYTE, dummy);
		}
		
		/*
		=======================
		GL_EndBuildingLightmaps
		=======================
		*/
		internal virtual void  GL_EndBuildingLightmaps()
		{
			LM_UploadBlock(false);
			GL_EnableMultitexture(false);
		}
		
		
		//ImageFrame frame;
		
		//	void debugLightmap(byte[] buf, int w, int h, float scale) {
		//		IntBuffer pix = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
		//		
		//		int[] pixel = new int[w * h];
		//		
		//		pix.get(pixel);
		//		
		//		BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
		//		image.setRGB(0,  0, w, h, pixel, 0, w);
		//		AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(scale, scale), AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
		//		BufferedImage tmp = op.filter(image, null);
		//		
		//		if (frame == null) {
		//			frame = new ImageFrame(null);
		//			frame.show();
		//		} 
		//		frame.showImage(tmp);
		//		
		//	}
	}
}